﻿using System;
using System.Collections.Generic;
using TSharp.Core.Osgi;
using TSharp.Core.Repository;
[assembly: TSharp.Core.Osgi.RegService(typeof(IReadonlyRepository<>), typeof(ReadonlyRepository<>))]
namespace TSharp.Core.Repository
{
    /// <summary>
    /// Class ReadonlyRepository
    /// </summary>
    /// <typeparam name="TEntity">The type of the entity.</typeparam>
    /// <remarks>tangj15 at 2012-5-4 13:12</remarks>
    public class ReadonlyRepository<TEntity> : IReadonlyRepository<TEntity>
        where TEntity : class
    {

        /// <summary>
        /// Initializes a new instance of the <see cref="ReadonlyRepository&lt;TEntity&gt;"/> class.
        /// </summary>
        public ReadonlyRepository() :
            this(AopContext.BeginRequestReadonlyUnitOfWork())
        {

        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="unitOfWork">Unit of work for concrete implementation of data mapper.</param>
        protected ReadonlyRepository(IUnitOfWork unitOfWork)
        {
            this.EnsureNotNull(unitOfWork);

            this.SpecificationLocator = LazyLoading.New<ISpecificationLocator>();
            this.UnitOfWork = unitOfWork;
        }

        /// <summary>
        /// Checks if given instance is not null. Use the method to validate input parameters.
        /// </summary>
        protected void EnsureNotNull(object o)
        {
            if (o == null)
            {
                throw new ArgumentNullException("o", "Argument can not be null.");
            }
        }

        /// <summary>
        /// Gets specification locator for the repository to resolve specifications.
        /// </summary>
        protected ISpecificationLocator SpecificationLocator { get; private set; }

        /// <summary>
        /// Gets unit of work the repository operates on.
        /// </summary>
        protected IUnitOfWork UnitOfWork { get; private set; }
        /// <summary>
        /// Gets entity from the repository by given id.
        /// </summary>
        /// <param name="id">Primary key that identifies the entity.</param>
        public virtual TEntity GetById(object id)
        {
            return this.UnitOfWork.GetById<TEntity>(id);
        }

        ///// <summary>
        ///// Gets all entities from the repository.
        ///// </summary>
        //public virtual IList<TEntity> GetAll()
        //{
        //    return this.UnitOfWork.GetAll<TEntity>();
        //}

        /// <summary>
        /// Gets specification that allows to filter only requested entities
        /// from the repository.
        /// </summary>
        /// <typeparam name="TSpecification">Concrete specification that will be resolved
        /// and initialized with underlying unit of work instance. This ensures fluent 
        /// and strongly typed way of connecting repository (uow) and specifications.</typeparam>
        public virtual TSpecification Specify<TSpecification>()
            where TSpecification : class, ISpecification<TEntity>
        {
            TSpecification specification = default(TSpecification);

            try
            {
                specification = this.SpecificationLocator.Resolve<TSpecification, TEntity>();
            }
            catch (Exception ex)
            {
                throw new GenericRepositoryException(
                    string.Format(
                        "Could not resolve requested specification {0} for entity {1} from the specification locator."
                        , typeof(TSpecification).FullName
                        , typeof(TEntity).FullName
                        )
                    , ex
                    );

            }

            specification.Initialize(UnitOfWork);
            return specification;
        }


        /// <summary>
        /// Specifies this instance.
        /// </summary>
        /// <returns></returns>
        public ISpecification<TEntity> Specify()
        {
            return Specify<ISpecification<TEntity>>();
        }
    }
}